#include <iostream>
#include <exception>
using std::exception;

#include <Poco/Net/SocketAddress.h>
#include <Poco/Net/DialogSocket.h>
#include <Poco/Net/ServerSocket.h>
#include <Poco/Exception.h>

#include "slave.h"

using Poco::Net::ServerSocket;
using Poco::Net::DialogSocket;
using Poco::IllegalStateException;
using std::cout;
using std::endl;

#include "command/network/joincommand.h"
using Commands::JoinCommand;

#include "command/network/runcommand.h"
using Commands::RunCommand;

#include "command/network/putcommand.h"
using Commands::PutCommand;

#include "command/network/getcommand.h"
using Commands::GetCommand;

#include "command/network/leavecommand.h"
using Commands::LeaveCommand;

#include "command/unknowncommand.h"
using Commands::UnknownCommand;

#include "Poco/Thread.h"
using Poco::Thread;

Slave::Slave( int port, CommandQueue* queue ) :
	fServerSocket(0),
	fCommFac(),
	fCurConn(),
	fQueue(queue),
	fPort( port ),
	_connected(false)
{
	/*
	 * First we need to register all the commands that we want to use.
	 */
	cout << "Slave :: Registering commands" << endl;
	fCommFac.registerCommand(new JoinCommand());
	fCommFac.registerCommand(new RunCommand("x x"));
	fCommFac.registerCommand( new PutCommand("", 0));
	fCommFac.registerCommand( new GetCommand( "" , 0 ));
	fCommFac.registerCommand(new LeaveCommand());
	cout << "Slave :: Created object" << endl;
}

Slave::~Slave() {
	cout << "Slave :: Destroying Slave object " << endl;
	for (vector<ConnectionHandler*>::iterator it = fHandlers.begin(); it != fHandlers.end(); ++it)
		delete *it;
	delete fServerSocket;
	delete fQueue;
	delete fCurConn;
	cout << "Slave :: Slave object destroyed " << endl;
}

void Slave::run() {
	try {
		// We create a connection
		cout << "Slave :: Creating ServerSocket" << endl;
		fServerSocket = new ServerSocket(fPort);
		cout << "Slave :: Waiting for connection" << endl;
		cout << "Slave :: Port ::" << fPort << endl;
		cout << "Slave :: " << fServerSocket->address().host().toString() << endl;
		while(true)
		{
			// We initiate the connectionhandler on the reception of a join command.
			cout << "Slave :: Creating a Dialogsocket so I can have a decent conversation." << endl;
			DialogSocket* fDialog = new DialogSocket(fServerSocket->acceptConnection());
			if (_connected)
			{
				cout << "Slave :: It appears we are already connected" << endl;
				fCurConn = fDialog;
			} else {
				cout << "Slave :: Oooh, a new connection" << endl;
				_connected = true;
			}
			fHandlers.push_back(new ConnectionHandler(fDialog, this));
			//fHandler = handler;
			cout << "Slave :: New ConnectionHandler created" << endl;
			// We start the connectionhandler thread while it does not recieve a leave command.
		}
	 	cout << "Slave :: Going to quit" << endl;
		stop();
	} catch ( Poco::Exception e ) {
		cout <<"Slave :: Something went wrong, I'm quitting" << endl;
		cout << "Slave :: "<< e.displayText() << endl;
		stop();
		throw;
	}

}

void Slave::executeCommand(Command* command)
{
	command->execute();
}

void Slave::stop() {
	if (fServerSocket) {
		cout << "Slave :: Slave stopping." << endl;
		fServerSocket->close();
		_connected = false;
		cout << "Slave :: Socket closed & deleted.";
	}
}

void Slave::push_up(Command* cmd)
{
	if (fQueue)
	{
		cout << "Slave :: pushed up a command" << endl;
		fQueue->push(cmd);
	} else {
		cout << "Slave :: did not push up a command" << endl;
	}
}

Slave::ConnectionHandler::ConnectionHandler(DialogSocket* socket, Slave* parent) :
	_handler("handler"),
	_sock(socket),
	_parent(parent)
{
	cout << "ConnectionHanlder :: Starting up" << endl;
	_handler.start(*this);
	//_handler.join();
	cout << "ConnectionHandler :: Is our thread running?" << (_handler.isRunning()?" Yes" : " No") << endl;
}

Slave::ConnectionHandler::~ConnectionHandler(){
	cout << "ConnectionHandler :: Destructing ConnectionHandler object" << endl;
	delete _parent;
	cout << "ConnectionHandler :: Object Destroyed" << endl;
}

void Slave::ConnectionHandler::run()
{
	string const remotehost = _sock->peerAddress().host().toString();
	// The ConnectionHandler will run as long as it does not recieve a leave command.
	cout << "ConnectionHandler :: Starting ConnectionHandler Thread" << endl;
	cout << "ConnectionHandler :: " << remotehost << endl;
	LeaveCommand* begone = 0;
	while(begone == 0) {
		cout << "ConnectionHandler (" << remotehost << ") :: Waiting for a Message..." << endl;
		string msg = "";
		if ( _sock->receiveMessage(msg)){
			cout << "ConnectionHandler (" << remotehost << ") :: Message recieved!" << endl;
		}
		cout << "ConnectionHandler (" << remotehost << ") :: Received message: " << msg << endl;
		
		try {
			Command* cmd = _parent->fCommFac.makeCommand(msg, _sock);
			cout << "ConnectionHandler (" << remotehost << ") :: Do we have a command? " << (cmd?"yes":"no") << endl;
			cout << "ConnectionHandler (" << remotehost << ") :: Checking command " << cmd->name() << endl;
			_parent->executeCommand(cmd);
			begone = dynamic_cast<LeaveCommand*>(cmd);

			cout << "ConnectionHandler (" << remotehost << ") :: Get result of " << cmd->name() << endl;
			string response = cmd->result();
			cout << "ConnectionHandler (" << remotehost << ") :: Sending response: " << response << endl;
			_sock->sendMessage(response);
		} catch (NotImplemented e) {
			UnknownCommand* ucmd = new UnknownCommand(msg, _sock->peerAddress(), _sock->address());
			cout << "ConnectionHandler (" << remotehost << ") :: Pushing up an unknown command" << endl;
			_parent->push_up(ucmd);
			cout << "ConnectionHandler (" << remotehost << ") :: Getting result of unknown command" << endl;
			string response = ucmd->result();
			cout << "ConnectionHandler (" << remotehost << ") :: Sending response: " << response << endl;
			_sock->sendMessage(response);
		}
		cout << "ConnectionHandler (" << remotehost << ") :: Result sent" << endl;
	}
	cout << "ConnectionHandler (" << remotehost << ") :: ConnectionHandler exiting" << endl;
	_sock->close();
	cout << "ConnectionHandler (" << remotehost << ") :: DialogSocket closed" << endl;
}

